<?php

declare (strict_types=1);

namespace app\api\service\passport;

use app\common\enum\CointsType;
use app\common\library\helper;
use app\common\model\user\CointsLog;
use app\common\model\user\LoginLog;
use app\common\service\ShortSms;
use think\facade\Cache;
use think\facade\Config;
use app\api\model\User as UserModel;
use app\api\service\{
    user\Oauth as OauthService,
    passport\Party as PartyService};
use app\api\validate\passport\Login as ValidateLogin;
use app\common\service\BaseService;
use cores\exception\BaseException;


/**
 * 服务类：用户登录
 * Class Login
 * @package app\api\service\passport
 */
class Login extends BaseService
{
    private static $prefix = '福娃';
    /**
     * 用户信息 (登录成功后才记录)
     * @var UserModel|null $userInfo
     */
    private $userInfo;

    // 用于生成token的自定义盐
    const TOKEN_SALT = 'user_salt';
    const TOKEN_PREFIX = 'client_token:188888888';

    /**
     * 执行用户登录
     * @param array $data
     * @return bool
     * @throws BaseException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function login(array $data): bool
    {
        // 数据验证
        $this->validate($data);
        // 自动登录注册
        $this->register($data);
        // 记录登录态
        return $this->setSession();
    }

    /**
     * 自动登录注册
     * @param array $data
     * @return void
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function register(array $data): bool
    {
        $userInfo = UserModel::detail(['mobile' => $data['mobile']]);
        if (!$userInfo) {
            //新用户
            $userInfo = (new UserModel())::create([
                'mobile' => $data['mobile'],
                'nick_name' =>  helper::createId(4,self::$prefix),
                'platform' => getPlatform() ?? '',
                'reg_time' => time(),
                'reg_ip' => getIp() ?? request()->ip(),
                'login_status' => 1,
                'coins' => 10000,
            ]);
            if(!$userInfo->user_id){
                throwError('insert user error');
            }
            //加金币日志
            (new CointsLog())::write(
                CointsType::REGISTER_DONATE,
                $userInfo->user_id,
                10000,
                CointsType::data()[CointsType::REGISTER_DONATE]['name']
            );
            $userInfo = (new UserModel())->detail(['user_id' => $userInfo->user_id]);
        }else{
            //多设备登录处理
            $this->multipleDevicesHandle($userInfo);
        }
        //增加登录日志
        $token = $this->getToken((int)$userInfo->user_id);
        $this->setLoginLog(
            (int)$userInfo['user_id'],
            getIp() ?? request()->ip(),
            time(),
            $token
        );
        $this->userInfo = $userInfo;
        return true;
    }



    /**
     * 快捷登录：微信小程序用户
     * @param array $form
     * @return bool
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\Exception
     */
    public function loginMpWx(array $form): bool
    {
        // 获取微信小程序登录态(session)
        $wxSession = PartyService::getMpWxSession($form['partyData']['code']);

        // 判断openid是否存在
        $userId = OauthService::getUserIdByOauthId($wxSession['openid'], 'MP-WEIXIN');
        // 获取用户信息
        $userInfo = !empty($userId) ? UserModel::detail($userId) : null;
        if(empty($userInfo)) {
            //新用户
            $userInfo = (new UserModel())::create([
                'mobile' => $form['mobile'] ?? '',
                'nick_name' =>  helper::createId(4,self::$prefix),
                'platform' => getPlatform() ?? '',
                'reg_time' => time(),
                'reg_ip' => getIp() ?? request()->ip(),
                'login_status' => 1,
                'coins' => 10000,
            ]);
            if(!$userInfo->user_id){
                throwError('insert user error');
            }
            //加金币日志
            (new CointsLog())::write(
                CointsType::REGISTER_DONATE,
                $userInfo->user_id,
                10000,
                CointsType::data()[CointsType::REGISTER_DONATE]['name']
            );
            $userInfo = (new UserModel())->detail(['user_id' => $userInfo->user_id]);
            // 保存第三方用户信息
            $this->createUserOauth($this->getUserId(), true, $form['partyData']);
        }else{
            // 更新用户登录信息
            $this->multipleDevicesHandle($userInfo);
        }
        $this->userInfo = $userInfo;
        // 记录登录态
        return $this->setSession();

    }


    //多设备登录处理
    public function multipleDevicesHandle(UserModel $userInfo):bool
    {
        if($userInfo->login_status == 1){
            //将其他设备的token失效
            $login_log = (new LoginLog())->where('user_id',$userInfo->user_id)
                ->order(['log_id' => 'desc'])
                ->find();
            if($login_log){
                $redis = Cache::store('redis');
                $redis->select(0);
                $hash_key = self::TOKEN_PREFIX;
                $redis->hdel($hash_key,$login_log->access_token);
                //状态更改
                $login_log->status = -1;
                $login_log->save();
            }
        }
        return true;
    }



    /**
     * 保存oauth信息(第三方用户信息)
     * @param int $userId 用户ID
     * @param bool $isParty 是否为第三方用户
     * @param array $partyData 第三方用户数据
     * @return void
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function createUserOauth(int $userId, bool $isParty, array $partyData = []): void
    {
        if ($isParty) {
            $Oauth = new PartyService;
            $Oauth->createUserOauth($userId, $partyData);
        }
    }

    /**
     * 当前登录的用户信息
     */
    public function getUserInfo(): ?UserModel
    {
        return $this->userInfo;
    }

    /**
     * 当前登录的用户ID
     * @return int
     */
    private function getUserId(): int
    {
        return (int)$this->getUserInfo()['user_id'];
    }



    /**增加登录日志
     * @param $user_id
     * @param $login_ip
     * @param $login_time
     * @param $access_token
     * @return bool
     */
    public function setLoginLog($user_id, $login_ip, $login_time,$access_token): bool
    {
        $res = (new LoginLog())::create([
            'user_id' => $user_id,
            'login_type' => 1,
            'login_time' => $login_time,
            'login_ip' => $login_ip,
            'access_token' => $access_token
        ]);
        if(!$res->log_id){
            return false;
        }
        return true;
    }


//    /**
//     * 更新用户登录信息
//     * @param UserModel $userInfo
//     * @param bool $isParty 是否存在第三方用户信息
//     * @param array $partyData 用户信息(第三方)
//     * @return void
//     */
//    private function updateUser(UserModel $userInfo, bool $isParty, array $partyData = []): void
//    {
//        // 写入用户信息(第三方)
//        // 如果不需要每次登录都更新微信用户头像昵称, 下面4行代码可以屏蔽掉
//        // if ($isParty === true && !empty($partyData)) {
//        //     $partyUserInfo = PartyService::partyUserInfo($partyData, true);
//        //     $data = array_merge($data, $partyUserInfo);
//        // }
//        // 更新用户记录
//        $status = $userInfo->save([]) !== false;
//        // 记录用户信息
//        $this->userInfo = $userInfo;
//    }

    /**
     * 记录登录态
     * @return bool
     * @throws BaseException
     */
    public function setSession(): bool
    {
        empty($this->userInfo) && throwError('未找到用户信息');
        // 登录的token
        $token = $this->getToken($this->getUserId());
        $redis = Cache::store('redis');
        $user_info = [
            'user' => $this->userInfo,
            'is_login' => true,
        ];
        //设置用户信息
        $redis->select(0);
        $redis->hset(
            self::TOKEN_PREFIX,
            $token,
            json_encode($user_info,JSON_UNESCAPED_UNICODE)
        );
        //设置用户信息
        $redis->select((int)$this->getUserId() % 10);
        $redis->hmset('fw_user_info:'.$this->getUserId(),$this->userInfo->toArray());
        return true;
    }

    /**
     * 数据验证
     * @param array $data
     * @return void
     * @throws BaseException
     */
    private function validate(array $data): void
    {
        // 数据验证
        $validate = new ValidateLogin;
        if (!$validate->check($data)) {
            throwError($validate->getError());
        }
        if (!Config::get('app.debug') && $data['smsCode'] != '843169') {
            if(!(new ShortSms())->checkSms($data['smsCode'], $data['mobile'])){
                throwError('短信验证码不正确');
            }
        }

    }

    /**
     * 获取登录的token
     * @param int $userId
     * @return string
     */
    public function getToken(int $userId): string
    {
        static $token = '';
        if (empty($token)) {
            $token = $this->makeToken($userId);
        }
        return $token;
    }

    /**
     * 生成用户认证的token
     * @param int $userId
     * @return string
     */
    private function makeToken(int $userId): string
    {
        $storeId = $this->storeId;
        // 生成一个不会重复的随机字符串
        $guid = get_guid_v4();
        // 当前时间戳 (精确到毫秒)
        $timeStamp = microtime(true);
        // 自定义一个盐
        $salt = self::TOKEN_SALT;
        return md5("{$storeId}_{$timeStamp}_{$userId}_{$guid}_{$salt}");
    }
}